﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using WhsFacebook.Net.Db.SyncWithHomeServerLand.HomeServerLand;

namespace WhsFacebook.Net.Db.SyncWithHomeServerLand
{
    public sealed class Program
    {
        private static log4net.ILog _log = log4net.LogManager.GetLogger(typeof(Program));

        private readonly string _hslApiToken;
        private WhsFacebookEntities _entities;
        private readonly Queue<object> _delFromEntities = new Queue<object>();

        private void Run()
        {
            // prepare
            using (_entities = new WhsFacebookEntities())
            {
                SyncCategories();
                SyncAddins();
                SyncScreenshots();
                UploadUnknownAddins();

                // perform cleanup
                _log.Info("Deleting " + _delFromEntities.Count + " old objects...");

                while (_delFromEntities.Count > 0)
                {
                    _entities.DeleteObject(_delFromEntities.Dequeue());
                }

                _log.Info("Flushing...");

                _entities.SaveChanges();
            }
        }

        private void SyncCategories()
        {
            _log.Info("Syncing categories...");

            using (var ws = new HomeServerLand.HSLServicesSoapClient())
            {
                var src = ws.GetAddinCategoryList();
                var tgt = _entities.WhsCategories.ToArray();

                // delete old ones
                var obsolete = (
                    from e in tgt
                    where !src.Where(o => o.CategoryID == e.HslCategoryId).Any()
                    select e
                    );

                foreach (var e in obsolete)
                {
                    _log.Debug("  Obsolete: " + e.HslCategoryId);

                    _delFromEntities.Enqueue(e);
                }

                // add new ones
                var missing = (
                    from e in src
                    where !tgt.Where(o => o.HslCategoryId == e.CategoryID).Any()
                    select e
                    );

                foreach (var e in missing)
                {
                    _log.Debug("  Adding: " + e.CategoryID);

                    _entities.AddToWhsCategories(new WhsCategory()
                        {
                            HslCategoryId = e.CategoryID,
                            Title = e.CategoryName,
                            DescText = e.CategoryDescription,
                        });
                }

                // update old ones
                var existing = (
                    from e in tgt
                    select new { target = e, source = src.Where(o => o.CategoryID == e.HslCategoryId).FirstOrDefault() }
                    ).Where(o => o.source != null);

                foreach (var e in existing)
                {
                    var updated = false;

                    if (e.target.Title != e.source.CategoryName)
                    {
                        e.target.Title = e.source.CategoryName;

                        updated = true;
                    }

                    if (e.target.DescText != e.source.CategoryDescription)
                    {
                        e.target.DescText = e.source.CategoryDescription;

                        updated = true;
                    }

                    if (updated)
                        _log.Debug("  Updated: " + e.target.HslCategoryId);
                }

                _log.Info("Flushing...");

                _entities.SaveChanges();
            }
        }

        private void SyncAddins()
        {
            _log.Info("Syncing addins...");

            using (var ws = new HomeServerLand.HSLServicesSoapClient())
            {
                var src = ws.GetAddinList();
                var tgt = _entities.WhsAddins.ToArray();

                // delete old ones
                var obsolete = (
                    from e in tgt
                    where e.HslAddinId != null
                    && !src.Where(o => o.AddinID == e.HslAddinId).Any()
                    select e
                    );

                foreach (var e in obsolete)
                {
                    _log.Debug("  Obsolete: " + e.HslAddinId);

                    _delFromEntities.Enqueue(e);
                }

                // add new ones
                var missing = (
                    from e in src
                    where !tgt.Where(o => o.HslAddinId == e.AddinID).Any()
                    select e
                    );

                foreach (var e in missing)
                {
                    _log.Debug("  Adding: " + e.AddinID);

                    _entities.AddToWhsAddins(new WhsAddin()
                    {
                        Author = e.Author,
                        AuthorUrl = e.AuthorURL,
                        ChangeLog = e.ChangeLog,
                        ChangeLogUrl = e.ChangeLogURL,
                        DescText = e.Description,
                        DownloadCount = e.DownloadCount,
                        DownloadUrl = e.DownloadURL,
                        ForumUrl = e.ForumURL,
                        HslAddinId = e.AddinID,
                        HslCategoryId = e.CategoryID,
                        ProductCode = e.ProductCode,
                        Rating = e.Rating,
                        ReleaseDate = e.ReleaseDate,
                        Requirements = e.Requirements,
                        Title = e.Title,
                        UpdatedDate = e.UpdatedDate,
                        Version = e.Version,
                        LastSyncDate = DateTime.Now,
                    });
                }

                // update old ones
                var existing = (
                    from e in tgt
                    select new { target = e, source = src.Where(o => o.AddinID == e.HslAddinId).FirstOrDefault() }
                    ).Where(o => o.source != null);

                foreach (var e in existing)
                {
                    if (e.source.UpdatedDate <= e.target.UpdatedDate)
                        continue;

                    _log.Debug("  Updating: " + e.target.HslAddinId);

                    // update all values
                    e.target.Author = e.source.Author;
                    e.target.AuthorUrl = e.source.AuthorURL;
                    e.target.ChangeLog = e.source.ChangeLog;
                    e.target.ChangeLogUrl = e.source.ChangeLogURL;
                    e.target.DescText = e.source.Description;
                    e.target.DownloadCount = e.source.DownloadCount;
                    e.target.DownloadUrl = e.source.DownloadURL;
                    e.target.ForumUrl = e.source.ForumURL;
                    e.target.HslCategoryId = e.source.CategoryID;
                    e.target.ProductCode = e.source.ProductCode;
                    e.target.Rating = e.source.Rating;
                    e.target.ReleaseDate = e.source.ReleaseDate;
                    e.target.Requirements = e.source.Requirements;
                    e.target.Title = e.source.Title;
                    e.target.UpdatedDate = e.source.UpdatedDate;
                    e.target.Version = e.source.Version;
                    e.target.LastSyncDate = DateTime.Now;
                }

                _log.Info("Flushing...");

                _entities.SaveChanges();
            }
        }

        private void SyncScreenshots()
        {
            _log.Info("Gathering add-in screeshots...");

            using (var ws = new HomeServerLand.HSLServicesSoapClient())
            {
                var preSrc = _entities.WhsAddins.Where(o => o.HslAddinId != null).ToArray();
                var tgt = _entities.WhsScreenshots.ToArray();

                // gather source
                var src = new List<Screenshot>();
                var srcPos = -1;

                foreach (var s in preSrc)
                {
                    _log.DebugFormat("    Add-in: {0} ({1}/{2})", s.HslAddinId.Value, ++srcPos, preSrc.Length);

                    src.AddRange(ws.GetAddinScreenshot(s.HslAddinId.Value));
                }

                _log.Info("Syncing screenshots...");
                
                // delete old ones
                var obsolete = (
                    from e in tgt
                    where !src.Where(o => o.ScreenshotID == e.HslScreenshotId).Any()
                    select e
                    );

                foreach (var e in obsolete)
                {
                    _log.Debug("  Obsolete: " + e.HslScreenshotId);

                    _delFromEntities.Enqueue(e);
                }

                // add new ones
                var missing = (
                    from e in src
                    where !tgt.Where(o => o.HslScreenshotId == e.ScreenshotID).Any()
                    select e
                    );

                foreach (var e in missing)
                {
                    _log.Debug("  Adding: " + e.ScreenshotID);

                    _entities.AddToWhsScreenshots(new WhsScreenshot()
                    {
                        DescText = e.Description,
                        HslAddinId = e.AddinID,
                        Title = e.Name,
                        CreatedDate = e.CreatedDate,
                        HslScreenshotId = e.ScreenshotID,
                        ImageUrl = e.PhotoURL,
                        IsDefault = e.IsDefault,
                        LastSyncDate = DateTime.Now,
                    });
                }

                // update old ones
                var existing = (
                    from e in tgt
                    select new { target = e, source = src.Where(o => o.ScreenshotID == e.HslScreenshotId).FirstOrDefault() }
                    ).Where(o => o.source != null);

                foreach (var e in existing)
                {
                    var updated = false;

                    if (e.target.Title != e.source.Name)
                    {
                        e.target.Title = e.source.Name;

                        updated = true;
                    }

                    if (e.target.DescText != e.source.Description)
                    {
                        e.target.DescText = e.source.Description;

                        updated = true;
                    }

                    if (e.target.ImageUrl != e.source.PhotoURL)
                    {
                        e.target.ImageUrl = e.source.PhotoURL;

                        updated = true;
                    }

                    if (e.target.IsDefault != e.source.IsDefault)
                    {
                        e.target.IsDefault = e.source.IsDefault;

                        updated = true;
                    }

                    if (updated)
                        _log.Debug("  Updated: " + e.target.HslScreenshotId);
                }

                _log.Info("Flushing...");

                _entities.SaveChanges();
            }
        }

        private void UploadUnknownAddins()
        {
            _log.Info("Uploading unknown add-ins...");

            using (var ws = new HomeServerLand.HSLServicesSoapClient())
            {
                var un = _entities.WhsUnknownUserAddins;

                foreach (var a in un)
                {
                    try
                    {
                        _log.DebugFormat("  Posting: {0} ({1})", a.ProductCode, a.Title);

                        ws.PostAddinUnknown(
                        a.ProductCode,
                        a.Author,
                        a.Title,
                        a.Version,
                        _hslApiToken
                        );
                    }
                    catch (Exception x)
                    {
                        _log.Warn("Failed to post unknown add-in to HSL: " + a.ProductCode, x);
                    }
                }
            }
        }

        private Program()
        {
            _hslApiToken = ConfigurationManager.AppSettings["HomeServerLandToken"];
        }

        static void Main(string[] args)
        {
            _log.Info("Syncing add-ins catalog with Home Server Land...");

            try
            {
                new Program().Run();

                _log.Info("DONE.");
            }
            catch (Exception x)
            {
                _log.Fatal("Unhandled exception: " + x.Message, x);

                Environment.ExitCode = -1;
            }
        }
    }
}
